#pragma once

#include "Com.hpp"
#include "Log.hpp"
#include <functional>
#include "InetAddr.hpp"
#include <memory>
using namespace LogModule;

const static int gbacklog = 8;
class Socket
{
public:
    virtual void SocketOrDie() = 0;
    virtual void BindOrDie(uint16_t port) = 0;
    virtual void ListenOrDie(int backlog) = 0;
    virtual std::shared_ptr<Socket> Accept(InetAddr *client) = 0;
    virtual int Recv(std::string* out) = 0;
    virtual int Send(std::string& message) = 0;
    virtual void Close() = 0;
    virtual void Connect(InetAddr& server) = 0;
    void BuildTcpSocketMethod(uint16_t port, int backlog = gbacklog)
    {
        SocketOrDie();
        BindOrDie(port);
        ListenOrDie(backlog);
    }
    void BuildTcpClientSocketMethod()
    {
        SocketOrDie();
    }
};

class TcpSocket : public Socket
{
    public:
    TcpSocket()
    {

    }
    ~TcpSocket()
    {

    }
    TcpSocket(int fd)
        :_sockfd(fd)
    {

    }
    void SocketOrDie() override
    {
        _sockfd =socket(AF_INET , SOCK_STREAM , 0);
        if(_sockfd < 0)
        {
            LOG(LogLevel::FATAL) << "socket create failed";
            exit(SOCKET_ERR);
        }

        LOG(LogLevel::DEBUG) << "socket create success";
    }

    void  BindOrDie(uint16_t port) override
    {
        InetAddr server(port);
        int n = bind(_sockfd , server.NetAddrPtr() , server.GetLen());
        if(n < 0)
        {
            LOG(LogLevel::FATAL) <<"bind failed";
            exit(BIND_ERR);
        }

        LOG(LogLevel::DEBUG) <<"bind success";
    }

    void ListenOrDie(int backlog) override
    {
        int n = listen(_sockfd , backlog);
        if(n < 0)
        {
            LOG(LogLevel::ERROR) << "listen failed";
            exit(LISTEN_ERR);
        }

        LOG(LogLevel::DEBUG) <<"listen success";
    }

    std::shared_ptr<Socket> Accept(InetAddr *client) override
    {
        struct sockaddr_in peer;
        socklen_t len = sizeof(struct sockaddr_in);
        int sockfd = accept(_sockfd , COV(peer) , &len);
        if(sockfd < 0)
        {
            LOG(LogLevel::ERROR) <<"accept failed";
            return nullptr;
        }
        client->SetAddr(peer);

        LOG(LogLevel::DEBUG) << "accept success";
        std::shared_ptr<TcpSocket> sock = std::make_shared<TcpSocket>(sockfd);
        return sock;
    }

    int Recv(std::string* out) override
    {
        char buffer[1024];
        int n = recv(_sockfd , buffer , sizeof(buffer)-1 , 0);
        if(n < 0)
        {
            LOG(LogLevel::ERROR) << "recv failed";
        }
        buffer[n] = 0;
        *out = buffer;
        return n;
    }

    int Send(std::string& message)
    {
        return send(_sockfd , message.c_str() , message.size() , 0);
    }

    void Close()
    {
        if(_sockfd > 0)
        close(_sockfd);
    }

    void Connect(InetAddr& server)
    {
        int n = connect(_sockfd , server.NetAddrPtr() , server.GetLen());
        if(n < 0)
        {
            LOG(LogLevel::ERROR) <<"Connect failed";
        }
        LOG(LogLevel::ERROR) <<"Connect success";

    }
    private:
    int _sockfd;
};